////////////////////////////////
//
// DUST ACCUMULATION CLOSURE
// HEADER FILE
//
////////////////////////////////

#ifndef __BSDF_DUST_ACCUMULATION_H__
#define __BSDF_DUST_ACCUMULATION_H__
 
CCL_NAMESPACE_BEGIN

// Optional function used to describe intensity, or in this case,
// colour

// ShaderClosure *sc        The shader closure
// float3 color             The colour

__device float3 bsdf_dust_accumulation_get_intensity(const ShaderClosure *sc, const float3 color)
{
    return color;
}

// Henyey-Greenstein phase function - used to describe the dependence of
// scattering on deviation angle a
__device float HG(float a, float g)
{
    float scatDep = (1.0f - g^2.0f)/(1.0f + g^2.0f - 2.0f * g * a)^(3.0f/2.0f);

	return scatDep;
}

// All following functions are essential
// This sets the closure up
__device int bsdf_dust_accumulation_setup(ShaderClosure *sc)
{
    sc->data0 = max(sc->data0, 0.0f);

    sc->type = CLOSURE_BSDF_DUST_ACCUMULATION_ID;
    return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_GLOSSY;
}

// This can stay the same unless we're making a glossy closure
// float roughness          Roughness of material
__device void bsdf_dust_accumulation_blur(ShaderClosure *sc, float roughness)
{
}

// BRDF GOES HERE

// ShaderClosure *sc        The shader closure
// const float3 colors[8]   The colours that make up the ramp
// const float3 I           Shading position to viewing position
// const float3 omega_in    Light to shading position
// float *pdf               The probability distribution function in the
//                          direction I, given the parameters and the
//                          light's surface normal
__device float3 bsdf_dust_accumulation_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, float g, float thickness)
{
	// First step is to find the elevation and azimuth angles of the
	// light and view directions

	// Treat N as local z-axis, use to find local x-axis
	float3 N = sc->N;
	float3 localX = normalize(N.y, N.x*-1.0f, 0.0f);
	
	// Calculate elevation angles
	float azimuthI = (M_PI_F/2.0f)-acos(dot(N, I));
	float azimuthOmega_in = (M_PI_F/2.0f)-acos(dot(N, omega_in));
	
    // Calculate azimuth angles
	float elevationI = (M_PI_F/2.0f)-acos(dot(localX, cross(I, N)));
	float elevationOmega_in = (M_PI_F/2.0f)-acos(dot(localX, cross(omega_in, N)));
	
	// Calculate deviation angle between incident and exitant azimuth angles
	float a = azimuthI - azimuthOmega_in;
	
	// Use Henyey-Greenstein function to calculate scatter dependence
	float scatDep = HG(a, g);
	
	//Now we can find the dust radiance
	float3 radiance = scatDep*(cos(elevationOmega_in)) /
	                  cos(elevationOmega_in) + cos(elevationI);
	
	return radiance;
}

// BTDF GOES HERE
// No need to change unless we transmit light
__device float3 bsdf_dust_accumulation_transmit(const ShaderClosure *sc, const float3 colors[8], const float3 I, const float3 omega_in, float *pdf)
{
        return make_float3(0.0f, 0.0f, 0.0f);
}

// Describes how the closure is sampled
// float3 Ng                True geometric normal of the surface at P
// float3 dIdx              This has something to do with the
//                          derivative of I in relation to x
// float3 dIdy              This has something to do with the
//                          derivative of I in relation to y
// float randu              Used to help find the distribution over
//                          the hemisphere
// float randv              Uused to help find the distribution over
//                          the hemisphere
// float3 *eval             I think this is where things are sent to
//                          be evaluated for the color or intensity
// float3 *domega_in_dx     This is where the approximation for bounce
//                          goes, if there is a ray differential in
//                          relation to dx
// float3 *domega_in_dy     This is where the approximation for bounce
//                          goes, if there is a ray differential in
//                          relation to dy
__device int bsdf_dust_accumulation_sample(const ShaderClosure *sc, const float3 colors[8], float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
    float cosNO = dot(sc->N, I);
    float m_exponent = sc->data0;

    if (cosNO > 0) {
            // reflect the view vector
            float3 R = (2 * cosNO) * sc->N - I;

#ifdef __RAY_DIFFERENTIALS__
            *domega_in_dx = (2 * dot(sc->N, dIdx)) * sc->N - dIdx;
            *domega_in_dy = (2 * dot(sc->N, dIdy)) * sc->N - dIdy;
#endif

            float3 T, B;
            make_orthonormals (R, &T, &B);
            float phi = M_2PI_F * randu;
            float cosTheta = powf(randv, 1 / (m_exponent + 1));
            float sinTheta2 = 1 - cosTheta * cosTheta;
            float sinTheta = sinTheta2 > 0 ? sqrtf(sinTheta2) : 0;
            *omega_in = (cosf(phi) * sinTheta) * T +
                               (sinf(phi) * sinTheta) * B +
                               (            cosTheta) * R;
            if (dot(Ng, *omega_in) > 0.0f)
            {
                    // common terms for pdf and eval
                    float cosNI = dot(sc->N, *omega_in);
                    // make sure the direction we chose is still in the right hemisphere
                    if (cosNI > 0)
                    {
                            float cosp = powf(cosTheta, m_exponent);
                            float common = 0.5f * M_1_PI_F * cosp;
                            *pdf = (m_exponent + 1) * common;
                            float out = cosNI * (m_exponent + 2) * common;
                            *eval = bsdf_dust_accumulation_get_color(sc, colors, cosp) * out;
                    }
            }
    }
    return LABEL_REFLECT|LABEL_GLOSSY;
}

CCL_NAMESPACE_END
 
#endif /* __BSDF_DUST_ACCUMULATION_H__ */
